#include "log.h"
#include "dlist.h"
#include "network.h"
#include "mem_alloc.h"


#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>

#define CLOSESOCKET closesocket
#else
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef SOCKET
# define SOCKET int
# define INVALID_SOCKET  (SOCKET)(-1)
# define CLOSESOCKET close
#endif
#endif
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_SYS_EPOLL_H
# ifndef HAVE_EPOLL
#  define HAVE_EPOLL
# endif
#endif

#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif

#include <time.h>

#ifdef _DEBUG
#include <stdio.h>
#endif


#ifndef WIN32
int setnblock(int iSock)
{
    int iFlags;
    iFlags = fcntl(iSock, F_GETFL, 0);
    iFlags |= O_NONBLOCK;
    iFlags |= O_NDELAY;
    fcntl(iSock, F_SETFL, iFlags);
    return 0;
}
#else
int setnblock(SOCKET iSock)
{
    unsigned long ul = 1;
    int ret = ioctlsocket(iSock, FIONBIO, (unsigned long*)&ul);
    if(ret == SOCKET_ERROR)
    {
        return -1;
    }
    return 0;
}
#endif

static int setreuse(SOCKET socket)
{
    /*将socket设置为重用模式*/
    int flag = 1;
    if (setsockopt(socket, SOL_SOCKET,
                SO_REUSEADDR, (char *)&flag, sizeof(flag)) != 0) {
        return -1;
    }
    return 0;
}

static int socktoaddr(const char* host, unsigned short port, 
                      struct sockaddr_in *addr)
{
    struct hostent *host_ent;
    memset(addr, 0, sizeof(struct sockaddr_in));
    addr->sin_family = AF_INET;
    addr->sin_port   = htons(port);
    //获得主机信息入口
    if ((host_ent=gethostbyname(host))!=NULL) {
        memcpy(&addr->sin_addr, host_ent->h_addr, host_ent->h_length);
    } else {
        //xxx.xxx.xxx.xxx
        if ((addr->sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
            return -1;
        }
    }
    return 0;
}

SOCKET listen_on(const char * ip, unsigned short port)
{
	SOCKET fd;
    struct sockaddr_in addr;
    if (socktoaddr(ip, port, &addr) != 0) {
        return -1;
    }

    fd = socket(PF_INET, SOCK_STREAM, 0);

    if (fd == INVALID_SOCKET) {
        return INVALID_SOCKET;
    }

    if (setreuse(fd) != 0 || 
        setnblock(fd) != 0 ||
        bind(fd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ||
        listen(fd, 100) != 0) {
        CLOSESOCKET(fd);
        return INVALID_SOCKET;
    }
    return fd;  
}

SOCKET connect_to(const char * ip, unsigned short port, int * done)
{
	SOCKET fd;
    struct sockaddr_in addr;

#ifdef _WIN32
	DWORD error;
#endif

    if (socktoaddr(ip, port, &addr) != 0) {
        return -1;
    }

    fd = socket(PF_INET, SOCK_STREAM, 0);

    if (fd == INVALID_SOCKET) {
        return INVALID_SOCKET;
    }

    if (/*setreuse(fd) != 0 || */
        setnblock(fd) != 0) {
        CLOSESOCKET(fd);
        return INVALID_SOCKET;
    }
    
    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
        if (done) *done = 1;
        return fd;
    }
#ifdef _WIN32
	error = WSAGetLastError();
	if (error == WSAEINPROGRESS || error == WSAEWOULDBLOCK) {
        if (done) *done = 0;
        return fd;
	}
#else
    if (errno == EINPROGRESS||errno==EWOULDBLOCK) {
        if (done) *done = 0;
        return fd;
    }
#endif
    CLOSESOCKET(fd);
    return INVALID_SOCKET;
}

typedef struct socket_handle
{
    struct socket_handle * next;

    SOCKET fd;
    unsigned int mask;

    network_read_func on_read;
    network_write_func on_write;
    void * ctx;
} socket_handle;

struct network 
{
    int type;
    const char * desc;

#ifdef HAVE_EPOLL
    int efd;
#else
    fd_set rset;
    fd_set wset;
    fd_set eset;
#endif

    int exit;

    network_timer_func on_timer;
    void * timer_ctx;

    int mask;

    socket_handle ** handler;
    unsigned int fdlimit;
};

const char * network_desc(network * net)
{
    return net->desc;
}

network * network_create(int type)
{
    unsigned int i;
    network * net = 0;
#ifndef _WIN32
    struct rlimit l;
#endif

#ifdef HAVE_EPOLL
    int efd = -1;
#endif

#ifdef HAVE_EPOLL
    if (type != NETWORK_ET && type != NETWORK_LT) {
        return 0;
    }

    efd = epoll_create(10);
    if (efd == -1) {
        return 0;
    }
#else
    if (type != NETWORK_LT) {
        return 0;
    }
#endif

    net= GET_OBJECT(network);
    if (net == 0) {
#ifdef HAVE_EPOLL
        close(efd);
#endif
        return 0;
    }

    net->exit = 0;
#ifdef HAVE_EPOLL
    net->efd = efd;
#endif
    net->on_timer = 0;
    net->handler = 0;
 
#ifndef _WIN32
    if (getrlimit(RLIMIT_NOFILE, &l) == -1) {
        net->fdlimit = 1024;
    } else {
        net->fdlimit = l.rlim_cur;
    }
#else
	  net->fdlimit = 65535;
#endif

    net->handler = GET_OBJECT_N(socket_handle*, net->fdlimit);
    if (net->handler == 0) {
        RELEASE_OBJECT(network, net);
#ifdef HAVE_EPOLL
        close(efd);
#endif
        return 0;
    }

    for (i = 0; i < net->fdlimit; i++) {
        net->handler[i] = 0;
    }

    net->type = type;
    net->mask = 0;

#ifdef HAVE_EPOLL
    if (type == NETWORK_ET) {
        net->desc = "epoll ET";
    } else {
        net->desc = "epoll LT";
    }
#else
    net->desc = "select networkd";
#endif

    return net;
}

void network_exit(network * net)
{
    net->exit = 1;
}

static void on_connect_socket_active(network * net,
                                     SOCKET fd,
                                     network_connect_func cb,
                                     void * ctx);

static void on_listen_socket_active(network * net,
                                    SOCKET fd,
                                    network_accept_func cb,
                                    void * ctx);
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

#define MAX_SET(x, y) (((x) < (y)) && ((x) = (y)))
#define MIN_SET(x, y) (((x) > (y)) && ((x) = (y)))

#ifndef HAVE_EPOLL
unsigned int set_fd(network * net)
{
    unsigned int i, maxfd = 0;
    FD_ZERO(&net->rset);
    FD_ZERO(&net->wset);
    FD_ZERO(&net->eset);
    for (i = 0; i < net->fdlimit; i++) {
        if (net->handler[i]) {
            socket_handle * h = net->handler[i];
            if ((void*)h->on_read == (void*)on_listen_socket_active) {
                if (h->mask&NETWORK_READ) {
                    FD_SET(h->fd, &net->rset);
                    MAX_SET(maxfd, h->fd);
                }
                continue;
            }

            if ((h->mask&NETWORK_WRITE) && h->on_write) {
                FD_SET(h->fd, &net->wset);
				MAX_SET(maxfd, h->fd);
            }

            if ((h->mask&NETWORK_READ) && h->on_read) {
                FD_SET(h->fd, &net->rset);
				MAX_SET(maxfd, h->fd);
            }
        }
    }
    return maxfd;
}
#endif

int network_loop(network * net)
{
    while (!net->exit) {
        network_step(net);
    }
    net->exit = 0;
    return 0;
}


#ifdef HAVE_EPOLL
# define IS_READ(i) (events[i].events & EPOLLIN)
# define IS_WRITE(i) (events[i].events & EPOLLOUT)
#else
# define IS_READ(i) FD_ISSET(i, &net->rset) 
# define IS_WRITE(i) FD_ISSET(i, &net->wset)
#endif

int network_step(network * net)
{
    int i;
    static time_t last_time = 0;
    int fds = 0;
	time_t t;
	int loop_cnt;

#ifdef HAVE_EPOLL
    struct epoll_event events[10];
    fds = epoll_wait(net->efd, events, 10, 1*1000);
#else
    int maxfd = set_fd(net);
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 100 * 1000;
    if (maxfd >= 0) {
        fds = select(maxfd+1, &net->rset, &net->wset, &net->eset, &tv);
    } else {
        fds = select(maxfd+1, 0, 0, 0, &tv);
    }
#endif

    t = time(0);
    if (last_time != t) {
        last_time = t;
        //TODO:check timeout
        if ((net->mask&NETWORK_TIMER) && net->on_timer)  {
            net->on_timer(net, t, net->timer_ctx);
        }
    }

#ifdef HAVE_EPOLL
    loop_cnt = fds;
#else
    loop_cnt = maxfd+1; //net->fdlimit;
#endif
    for (i = 0; i < loop_cnt; i++) {
        SOCKET fd;
        int mask;
        void * ctx;
        network_read_func on_read;
        network_write_func on_write;

#ifdef HAVE_EPOLL
        socket_handle * h = (socket_handle*)events[i].data.ptr;
#else
		socket_handle * h;
        if (net->handler[i] == 0) {
            continue;
        }
        h = net->handler[i];
#endif
        fd = h->fd;
        mask = h->mask;
        ctx = h->ctx;
        on_read = h->on_read;
        on_write = h->on_write;

        if ((void*)on_read == (void*)on_connect_socket_active) {
            //connecting
            if ( IS_READ(i) || IS_WRITE(i) ) {
                on_connect_socket_active(net, fd,
                                         (network_connect_func)on_write,
                                         ctx);
            }
            continue;
        }

        if ((void*)on_read == (void*)on_listen_socket_active) {
            if (IS_READ(i)) {
                on_listen_socket_active(net, fd,
                                        (network_accept_func)on_write,
                                        ctx);
            }
            continue;
        }

        //connecting
        if (IS_READ(i)) {
            if ((mask&NETWORK_READ)&&on_read) {
                on_read(net, fd, ctx);
            }
        }

        if (IS_WRITE(i)) {
            if ((mask&NETWORK_WRITE) && on_write) {
                on_write(net, fd, ctx);
            }
        }
    }
    return 0;
}

void network_destory(network * net)
{
    if (net) {
        unsigned int i;
        for (i = 0; i < net->fdlimit; i++) {
			socket_handle * s;
            if (net->handler[i] == 0) {
                continue;
            }
            s = net->handler[i];
            RELEASE_OBJECT(socket_handle, s);
            net->handler[i] = 0;
        }
        RELEASE_OBJECT_N(socket_handle*, net->handler, net->fdlimit);
#ifdef HAVE_EPOLL
        close(net->efd);
#endif
        RELEASE_OBJECT(network, net);
    }
}

#ifdef HAVE_EPOLL
static int epoll_update(network * net, socket_handle * h, int add)
{
    struct epoll_event event;
    event.events = 0;
    event.data.ptr = h;
    if (h->on_read && (h->mask&NETWORK_READ)) {
        event.events |= EPOLLIN;
    }

    if (h->on_write && (h->mask&NETWORK_WRITE) ) {
        event.events |= EPOLLOUT;
    }

    if (net->type == NETWORK_ET) {
        event.events |= EPOLLET;
    }

    if (event.events!=0) {
        return epoll_ctl(net->efd,
                add?EPOLL_CTL_ADD:EPOLL_CTL_MOD,
                h->fd, &event);
    } else {
        return 0;
    }
}
#endif

static socket_handle * find_handle(network * net, SOCKET fd)
{
    return net->handler[fd];
}

static socket_handle * find_handle_or_add(network * net, SOCKET fd, int * add)
{
	socket_handle * h;

    if (net->handler[fd]) {
        if (add) *add = 0;
        return net->handler[fd];
    }

    if ((h = GET_OBJECT(socket_handle)) == 0) {
        if (add) *add = 0;
        return 0;
    }

    h->fd = fd;
    h->on_write = 0;
    h->on_read = 0;
    h->mask = 0;
    if (add) *add = 1;
 
    net->handler[fd] = h;
    return h;
}

static void on_connect_socket_active(network * net,
                                     SOCKET fd,
                                     network_connect_func cb,
                                     void * ctx)
{
    int error = 0;
#ifdef _WIN32
    int len = sizeof(error);
#else
	socklen_t len = sizeof(error);
#endif
    
	getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&error, &len);

    network_del(net, fd);
    if (cb) cb(net, fd, error, ctx);
    if (error != 0) {
        CLOSESOCKET(fd);
    }
}

SOCKET network_connect(network * net, 
                    const char * ip, int port,
                    network_connect_func on_connected,
                    void * ctx)
{
    int done = 0;
    SOCKET fd = connect_to(ip, port, &done);
    if (fd >= 0 && done) {
        on_connected(net, fd, 0, ctx);
    } else {
        if (network_set(net, fd,
                    (network_read_func)on_connect_socket_active,
                    (network_write_func)on_connected,
                    ctx) != 0) {
            CLOSESOCKET(fd);
            return INVALID_SOCKET;
        }
    }
    return fd;
}

static void on_listen_socket_active(network * net,
                                    SOCKET fd,
                                    network_accept_func cb,
                                    void * ctx)
{
    struct sockaddr_in addr;
#ifndef _WIN32
	socklen_t addr_len = sizeof(addr);
#else
	int addr_len = sizeof(addr);
#endif
    const char * ip = 0;
    unsigned short port = 0;
    SOCKET fd_acc;
retry:
    fd_acc = accept(fd, (struct sockaddr*)&addr, &addr_len);
    if (fd_acc == INVALID_SOCKET) {
#ifdef _WIN32
		int error = WSAGetLastError();
        if (error == WSAEWOULDBLOCK) {
            return;
        } else {
            WRITE_ERROR_LOG("accept failed : %d\n", error);
        }
#else
        if (errno == EINTR) {
            goto retry;
        } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return;
        } else {
            WRITE_ERROR_LOG("accept failed : %s\n", strerror(errno));
        }
#endif
        return;
    }

    ip = inet_ntoa(addr.sin_addr);
    port = ntohs(addr.sin_port);
    if (cb) cb(net, fd, fd_acc, ip, port, ctx);
    goto retry;
}

int network_set_listen_socket(network * net, SOCKET fd,
                   network_accept_func on_accept,
                   void * ctx)
{
    if (fd == INVALID_SOCKET) {
        return -1;
    }

    if( network_set(net, fd, (network_read_func)on_listen_socket_active,
                (network_write_func)on_accept, ctx) != 0) {
        return -1;
    }
    return 0;
}


SOCKET network_listen(network * net, 
        const char * ip, int port,
        network_accept_func on_accept,
        void * ctx)
{
    SOCKET fd = listen_on(ip, port);
    if (fd == INVALID_SOCKET) {
        return INVALID_SOCKET;
    }

    if (network_set_listen_socket(net, fd, on_accept, ctx) != 0) {
        CLOSESOCKET(fd);
        return INVALID_SOCKET;
    }
    return fd;
}
 
#ifdef HAVE_EPOLL
static int epoll_remove(network * net, socket_handle * h)
{
    struct epoll_event event;
    event.events = EPOLLIN|EPOLLOUT;
    event.data.ptr = h;

    if (net->type == NETWORK_ET) {
        event.events |= EPOLLET;
    }
    return epoll_ctl(net->efd, EPOLL_CTL_DEL, h->fd, &event);
}
#endif

int network_set(network * net, SOCKET fd,
                network_read_func on_read,
                network_write_func on_write,
                void * ctx)
{
	int add = 0;
	socket_handle * h;
    if (fd < 0) {
        return -1;
    }

    if (on_read == 0 && on_write == 0) {
        return -1;
    }

    if (setnblock(fd) != 0) {
        return -1;
    }


    h  = find_handle_or_add(net, fd, &add);
    if (h == 0) {
        return -1;
    }

    h->fd = fd;
    h->on_read = on_read;
    h->on_write = on_write;
    h->ctx = ctx;
    if (h->on_read == (void*)on_listen_socket_active) {
        h->mask = NETWORK_READ;
    } else {
        h->mask = NETWORK_READ|NETWORK_WRITE;
    }
#ifdef HAVE_EPOLL
    return epoll_update(net, h, add);
#else
    return 0;
#endif
}

int network_timer(network * net,
    network_timer_func on_timer,
    void * ctx)
{
    net->on_timer = on_timer;
    net->timer_ctx = ctx;
    net->mask |= NETWORK_TIMER;
    return 0;
}

int network_del(network * net, SOCKET fd)
{
    socket_handle * h = find_handle(net, fd);
    if (h == 0) {
        return -1;
    }

#ifdef HAVE_EPOLL
    if (epoll_remove(net, h) != 0 ) {
        return -1;
    }
#endif
    net->handler[fd] = 0;
    RELEASE_OBJECT(socket_handle, h);
    return 0;
}

int network_enable(network * net, SOCKET fd, int flag)
{
    socket_handle * h = find_handle(net, fd);
    if (h) {
        h->mask |= flag;
#ifdef HAVE_EPOLL
        return epoll_update(net, h, 0);
#else
        return 0;
#endif
    }
    return -1;
}

int network_disable(network * net, SOCKET fd, int flag)
{
    socket_handle * h = find_handle(net, fd);
    if (h) {
        h->mask &= ~flag;
#ifdef HAVE_EPOLL
        return epoll_update(net, h, 0);
#else
        return 0;
#endif
    }
    return -1;
}

int network_disable_timer(network * net)
{
    net->mask &= ~NETWORK_TIMER;
    return 0;
}

int network_enable_timer(network * net)
{
    net->mask |= NETWORK_TIMER;
    return 0;
}

void * network_userdata(network * net, SOCKET fd)
{
    socket_handle * h = find_handle(net, fd);
    return h ? h->ctx : 0;
}
